/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Extended Service Controller Class
 *		Extends the functionality of the ServiceController class.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using Microsoft.Win32;
using System.IO;
using System.Management;
using System.ServiceProcess;
using System.Diagnostics;
using System.Threading;
using Adaptive.Foundation.Properties;

namespace Adaptive.Foundation.SysTools
{
    /// <summary>
    /// Extends the functionality of the <see cref="ServiceController"/> class.
    /// </summary>
    public class ServiceControllerEx : ServiceController
    {
        /*------------------------------------------------------------------------
         * Private Member Declarations
         *----------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Log-on-as user id.
        /// </summary>
        private string _logOn = string.Empty;
        /// <summary>
        /// Service description value.
        /// </summary>
        private string _description = string.Empty;
        /// <summary>
        /// Path value.
        /// </summary>
        private string _path = string.Empty;
        /// <summary>
        /// Command parameters.
        /// </summary>
        private string _commandParams = string.Empty;
        /// <summary>
        /// Process name.
        /// </summary>
        private string _procName = string.Empty;
        /// <summary>
        /// Service process object.
        /// </summary>
        private Process _process;
        /// <summary>
        /// Service start-up type / enabled/disabled indicator.
        /// </summary>
        private ServiceStartupType _startType = ServiceStartupType.Unknown;
        #endregion

        /*------------------------------------------------------------------------
		 * Constructor / Dispose / Destructor Methods
		 *----------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceControllerEx"/> 
        /// class that is not associated with a specific service.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public ServiceControllerEx()
            : base()
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceControllerEx"/> 
        /// class that is associated with an existing service on the local computer.
        /// </summary>
        /// <param name="serviceName">The short name that identifies the service to the system.</param>
        ///<exception cref="ArgumentException">
        /// The name parameter is a <b>null</b> reference (<b>Nothing</b> in Visual Basic) or 
        /// has length zero (0).
        /// </exception>
        public ServiceControllerEx(string serviceName)
            : base(serviceName)
        {
            GetRegistrySettings();
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceControllerEx"/> 
        /// class that is associated with an existing service on the specified computer.
        /// </summary>
        /// <param name="serviceName">The short name that identifies the service to the system.</param>
        /// <param name="machineName">The computer on which the service resides.</param>
        ///<exception cref="ArgumentException">
        /// The name parameter is a <b>null</b> reference (<b>Nothing</b> in Visual Basic) or 
        /// has length zero (0).
        /// -or-
        ///
        /// The <i>machineName</i> parameter syntax is invalid.
        /// </exception>
        /// <remarks>
        /// For the <i>machineName</i> parameter, you can use "." to represent the local computer.
        /// </remarks>
        public ServiceControllerEx(string serviceName, string machineName)
            :
            base(serviceName, machineName)
        {
            GetRegistrySettings();
        }
        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="ServiceControllerEx"/> and 
        /// optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">
        /// <b>true</b> to release both managed and unmanaged resources; 
        /// <b>false</b> to release only unmanaged resources.
        /// </param>
        /// <remarks>
        /// This method is called by the public <see cref="Dispose"/>() method and the 
        /// <see cref="object.Finalize"/> method. <b>Dispose()</b> invokes the protected 
        /// <b>Dispose(Boolean)</b> method with the <i>disposing</i> parameter set to 
        /// <b>true.</b> <b>Finalize</b> invokes <b>Dispose</b> with <i>disposing</i> set 
        /// to <b>false</b>.
        ///
        /// When the <i>disposing</i> parameter is true, this method releases all 
        /// resources held by any managed objects that this <b>DirectoryPath</b> references. 
        /// This method invokes the <b>Dispose()</b> method of each referenced object.
        ///
        /// <b>Notes to Inheritors:</b>  <b>Dispose()</b> can be called multiple times 
        /// by other objects. When overriding <b>Dispose(Boolean)</b>, be careful not to 
        /// reference objects that have been previously disposed of in an earlier 
        /// call to <b>Dispose</b>. For more information about how to implement 
        /// <b>Dispose(Boolean)</b>, see <b>Implementing a Dispose Method</b>.
        /// </remarks>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ReleaseObjects();
            }
            _logOn = null;
            _description = null;
            _path = null;
            _commandParams = null;
            _procName = null;
            _process = null;
            base.Dispose(disposing);
            GC.SuppressFinalize(this);
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Public Properties
		 *----------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the login ID value for the service.
        /// </summary>
        /// <value>
        /// The user ID used by the service when authenticating.
        /// </value>
        public string LogOnAs
        {
            get
            {
                return _logOn;
            }
        }
        /// <summary>
        /// Gets the description of the service.
        /// </summary>
        /// <value>
        /// A service description.
        /// </value>
        public string Description
        {
            get
            {
                return _description;
            }
        }
        /// <summary>
        /// Gets the fully-qualified path of the executable image for the service.
        /// </summary>
        /// <value>
        /// A path and executable file name if the service is present; otherwise, 
        /// a blank string.
        /// </value>
        public string ImagePath
        {
            get
            {
                return _path;
            }
        }
        /// <summary>
        /// Gets the name of the process used to execute the specified service.
        /// </summary>
        /// <value>
        /// The name of the service process, if executing.
        /// </value>
        public string ProcessName
        {
            get
            {
                return _procName;
            }
        }
        /// <summary>
        /// Gets the command line parameters used when starting the service.
        /// </summary>
        /// <value>
        /// The command line parameters as specified by the registry entry.
        /// </value>
        public string CommandLineParameters
        {
            get
            {
                return _commandParams;
            }
        }
        /// <summary>
        /// Gets the <see cref="ProcessName"/> object associated with the service being
        /// represented, if executing.
        /// </summary>
        /// <value>
        /// A <see cref="Process"/> instance if available; otherwise, 
        /// <b>null</b>.
        /// </value>
        public Process Process
        {
            get
            {
                return _process;
            }
        }
        /// <summary>
        /// Gets or sets the startup type of the service.
        /// </summary>
        /// <value>
        /// A <see cref="ServiceStartupType"/> enumerated value.
        /// </value>
        public ServiceStartupType StartupType
        {
            get { return _startType; }
            set
            {
                _startType = value;
                WriteStartUpType((int)_startType);
            }
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Public Methods / Functions
		 *----------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Refreshes all the property values, setting them to the current values.
        /// </summary>
        /// <remarks>
        /// <b>Refresh</b> sets the <see cref="ServiceController"/> properties back to 
        /// their default values. The method also sets the <see cref="ServiceController.ServicesDependedOn"/> 
        /// and <see cref="ServiceController.DependentServices"/> properties to a <b>null</b> reference (
        /// <b>Nothing</b> in Visual Basic).
        /// </remarks>
        public new void Refresh()
        {
            //Release current settings.
            ReleaseObjects();

            //Refresh content.
            base.Refresh();
            GetRegistrySettings();
            StartProcessThread();
        }
        /// <summary>
        /// Gets the list of all services.
        /// </summary>
        /// <returns>
        /// A <see cref="ServiceControllerEx"/> array containing an object instance
        /// for each Windows Service.
        /// </returns>
        public static new ServiceControllerEx[] GetServices()
        {
            ServiceController[] originalList = null;		//Original list.
            ServiceControllerEx newItem = null;				//New object instance.
            ServiceControllerEx[] returnValue = null;		//Return value.
            int count = 0;									//Iteration counter.
            int length = 0;									//Iteration limit.

            originalList = ServiceController.GetServices();
            length = originalList.Length;
            returnValue = new ServiceControllerEx[length];

            for (count = 0; count < length; count++)
            {
                newItem = new ServiceControllerEx(originalList[count].ServiceName);
                returnValue[count] = newItem;
            }

            //Clear memory.
            newItem = null;
            originalList.Initialize();
            Array.Clear(originalList, 0, length);

            return returnValue;
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Private Methods / Functions
		 *----------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Gets the process name and command line parameters from the current 
        /// path setting.
        /// </summary>
        private void SetProcessName()
        {
            int position = 0;               //Character position indicator.

            _path = _path.Replace("\"", string.Empty);
            position = _path.IndexOf("-");
            if (position != -1)
            {
                _commandParams = _path.Substring(position,
                    (_path.Length - position)).Trim();
                _path = _path.Substring(0, position).Trim();
            }
            _procName = Path.GetFileName(_path);
        }
        /// <summary>
        /// Reads the local registry settings for the current service process.
        /// </summary>
        public void GetRegistrySettings()
        {
            RegistryKey key = null;			                        //Registry key to open.
            string keyValue = Resources.RegistryBaseKeyName  +      
                              ServiceName + @"\";                   //Registry key name.

            try
            {
                //Open the specified key.
                key = RegistryOps.OpenLocalMachineRegistryKey(keyValue);
                if (key != null)
                {
                    //Read values from registry.
                    _logOn = key.GetValue(Resources.ObjectNameValue, string.Empty).ToString();
                    _description = key.GetValue(Resources.DescriptionValue).ToString();
                    _path = key.GetValue(Resources.ImagePathValue).ToString();
                    _startType = (ServiceStartupType)Convert.ToInt32(key.GetValue(Resources.StartValue));

                    //Find the process.
                    SetProcessName();
                    key.Close();
                }
            }
            catch
            { }
        }
        /// <summary>
        /// Disposes of objects in use.
        /// </summary>
        private void ReleaseObjects()
        {
            //Deattach process object.
            if (_process != null)
            {
                _process.Close();
                _process.Dispose();
            }
            _process = null;
        }
        /// <summary>
        /// Locates the associated service process, if executing.
        /// </summary>
        private void GetServiceProcess()
        {
            ManagementObjectSearcher searchEngine = null;	//Search container.
            ManagementObjectCollection list = null;			//Result list.
            int processId = -1;								//ID of executing process.

            try
            {
                //Create search object and get query results.
                searchEngine = new ManagementObjectSearcher(
                    Resources.ProcessNameQuery + ServiceName + "'");
                list = searchEngine.Get(); 

                //Look for each item and find the process Id.
                foreach (ManagementObject serviceProcess in list)
                {
                    processId = Convert.ToInt32(Convert.ToUInt32(
                        serviceProcess.GetPropertyValue(Resources.ProcessIDValue).ToString()));
                }

                //If a valid ID is returned, get an object wrapper for the process.
                if (processId != -1)
                {
                    try
                    {
                        _process = Process.GetProcessById(processId);
                    }
                    catch
                    {
                        _process = null;
                    }
                }
                
                //Clear and return.
                list.Dispose();
                searchEngine.Dispose();
            }
            catch
            { }
        }
        /// <summary>
        /// Starts the thread which finds the associated service process.
        /// </summary>
        private void StartProcessThread()
        {
            Thread newThread = null;		//Thread to execute.

            if ((Status != ServiceControllerStatus.Stopped))
            {
                newThread = new Thread(new ThreadStart(GetServiceProcess));
                newThread.Start();
            }
            newThread = null;
        }
        /// <summary>
        /// Writes a new start-up type value to the registry.
        /// </summary>
        /// <param name="startType">
        /// The integer value to write to the registry.
        /// </param>
        private void WriteStartUpType(int startType)
        {
            RegistryKey key = null;			//Registry key to open.
            string keyValue =
                Resources.RegistryBaseKeyName + ServiceName + "\\";        //Key name.

            try
            {
                key = RegistryOps.OpenLocalMachineRegistryKey(keyValue);
                if (key != null)
                {
                    key.SetValue(Resources.StartValue, startType);
                    key.Close();
                }
            }
            catch
            { }
        }
        #endregion
    }
}
